package com.jiang.utils;

import java.io.File;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.jiang.vo.ColumnAndProperty;

public class MySession {

	// 保存核心配置文件中的属性 这里如果要线程安全肯定new一个MySession多例
	private Map<String, String> hibernateCfgProperties = new HashMap<String, String>();
	private List<String> hbmList = new ArrayList<String>();
	private ColumnAndProperty columAndPro_id = new ColumnAndProperty(); // 保存主键对象
	private Map<String, ColumnAndProperty> columnAndPros = new HashMap<String, ColumnAndProperty>();
	private String pro_classPath = "";
	private String table; // 表名称
	private String catalog; // 模式
	private List<String> param_orders = new ArrayList<>(); // 存放动态设置参数值的顺序集合

	public MySession() {

	}

	/**
	 * 保存实体的方法, 只是针对单表的操作
	 * 
	 * @param vo
	 * @return
	 */
	public Serializable save(Object vo) {

		Connection conn = null;
		PreparedStatement ps = null;
		Serializable id = null;
		// 1. 寻找hibernate.cfg.xml 核心配置文件，获取数据库连接的相关信息
		File hibernateCfgFile = getHibernateConfigFile();
		// 2. 解析hibernate.cfg.xml核心配置文件
		if (hibernateCfgFile != null) {
			parseHibernateCfgFile(hibernateCfgFile);
			// 3. 寻找hbm.xml
			File hbmFile = getHbmFile(vo);
			if (hbmFile != null) {
				// 4. 解析hbm.xml
				parseHbmFile(hbmFile);
				// 5. 解析完hbm.xml后，开始获取连接操作数据库
				try {
					conn = this.getConnection();
					// 6. 获取连接后，创建动态SQL
					String sql = this.createSQL(conn, vo);
					System.out.println(sql);
					// 7. 设置语句的?的值
					if (StringUtils.isNotBlank(sql)) {
						ps = conn.prepareStatement(sql);
						this.setValue(ps, vo);
						// 8. 执行添加方法
						ps.executeUpdate();
						// 9. 查询最后一次插入的主键数据
						String sql_last = " SELECT LAST_INSERT_ID() ";
						ps = conn.prepareStatement(sql_last);
						ResultSet rs = ps.executeQuery();
						while (rs.next()) {
								switch (this.columAndPro_id.getType()) {
	
								case "java.lang.Integer": {
									id = rs.getInt(1);	
									break;
								}
								case "java.lang.String": {
									id = rs.getString(1);
									break;
								}
							}
						}
						if(id!=null){
							Field idField = vo.getClass().getDeclaredField(this.columAndPro_id.getPropertyName()) ;
							// 设置回去实体属性
							Method method = vo.getClass().getDeclaredMethod("set"+StringUtils.capitalize(this.columAndPro_id.getPropertyName()), idField.getType());
							method.invoke(vo, id);
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					close(conn,ps);
				}
			}
		}
		return id;
	}

	private void close(Connection conn, PreparedStatement ps){
		// 关闭连接
		if (conn != null) {
			try {
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		if (ps != null) {
			try {
				ps.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * @设置ps中的占位符的值
	 * @param ps
	 * @throws SQLException
	 */
	private void setValue(PreparedStatement ps, Object vo) throws Exception {
		int count = 1;
		for (int i = 0; i < param_orders.size(); i++) {
			String pro = param_orders.get(i);
			String type = null;
			if (pro.equals(this.columAndPro_id.getPropertyName())) {
				// 主键字段要另外设置
				type = columAndPro_id.getType();
			}
			// 通过属性名称判断类型
			type = this.columnAndPros.get(pro).getType();
			Object value = vo.getClass().getDeclaredMethod("get" + StringUtils.capitalize(pro)).invoke(vo);
			switch (type) {

			case "java.lang.Integer": {
				if (value != null) {
					Integer v = (Integer) value;
					ps.setInt(count, v);
					break;
				}
			}
			case "java.lang.String": {
				if (value != null) {
					String v = (String) value;
					ps.setString(count, v);
					break;
				}

			}
			case "java.util.Date": {
				if (value != null) {
					Date v = (Date) value;
					ps.setDate(count, new java.sql.Date(v.getTime()));
					break;
				}

			}
			case "java.lang.Double": {
				if (value != null) {
					Double v = (Double) value;
					ps.setDouble(count, v);
					break;
				}

			}
			}
			count++;
		}

		count = 0; // 重置
	}

	/**
	 * 动态创建SQL 组成SQL语句返回
	 * 
	 * @param conn
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public String createSQL(Connection conn, Object vo) throws Exception {

		StringBuffer sb = new StringBuffer();
		String tableName = this.table;
		String dataBaseName = this.catalog;
		sb.append(" INSERT INTO ").append(dataBaseName + ".").append(tableName).append("(");
		if (this.columnAndPros.size() > 0 || this.columAndPro_id.getIsId()) {
			// 判断是否是自动增长策略
			if (!"native".equalsIgnoreCase(this.columAndPro_id.getGenerator())) {
				// 如果不是自动增长策略，我们应该要单独设置
				sb.append(this.columAndPro_id.getColumnName() + ",");
				this.param_orders.add(this.columAndPro_id.getColumnName());
			}
			if (columnAndPros.size() > 0) {
				for (Map.Entry<String, ColumnAndProperty> entry : columnAndPros.entrySet()) {
					// 这里只是设置属性
					Object value = vo.getClass().getDeclaredMethod("get" + StringUtils.capitalize(entry.getKey()))
							.invoke(vo);
					if (value != null) {
						sb.append(entry.getKey() + ",");
						this.param_orders.add(entry.getKey()); // 添加到增加顺序中去
					}
				}
			}
			// 去除最后的逗号
			sb.delete(sb.length() - 1, sb.length());
			sb.append(") VALUES (");
			// 紧接着设置value值
			if (param_orders.size() > 0) {
				for (int i = 0; i < param_orders.size(); i++) {
					sb.append("?,");
				}
				sb.delete(sb.length() - 1, sb.length()).append(") ");
			} else {
				sb = new StringBuffer();
			}

		} else {
			sb = new StringBuffer();
		}

		return sb.toString();
	}

	/**
	 * 获取数据库连接
	 * 
	 * @return
	 * @throws Exception
	 */
	public Connection getConnection() throws Exception {
		Class.forName(this.hibernateCfgProperties.get("driver_class"));
		String url = this.hibernateCfgProperties.get("url");
		String username = this.hibernateCfgProperties.get("username");
		String password = this.hibernateCfgProperties.get("password");
		Connection conn = DriverManager.getConnection(url, username, password);
		return conn;
	}

	/**
	 * @获取核心配置文件
	 * @return
	 */
	private File getHibernateConfigFile() {
		File cfgFile = null;
		// 首先获取安装JDK后的系统全部属性信息
		// System.out.println(System.getProperties());
		// 系统全属性信息中发现是有classPath的，所以拿出该属性
		String classPath = System.getProperty("java.class.path");
		String[] eachParm = classPath.split(File.pathSeparator);
		if (eachParm != null && eachParm.length > 0) {
			for (int i = 0; i < eachParm.length; i++) {
				File dir = new File(eachParm[i]);
				if (dir.isDirectory()) { // 如果是目录
					cfgFile = new File(dir, "hibernate.cfg.xml");
					if (cfgFile.exists()) {
						pro_classPath = eachParm[i];
						return cfgFile;
					} else {
						cfgFile = null;
					}
				}
			}
		}
		return cfgFile;
	}

	/**
	 * @解析hibernate.cfg.xml核心配置文件
	 * @param file
	 */
	private void parseHibernateCfgFile(File file) {
		// 使用DOM4j解析配置文件 SAXReader
		SAXReader sReader = new SAXReader();
		try {
			Document document = sReader.read(file);
			Element root = document.getRootElement();
			// 获取session-factory元素
			Element sf = root.element("session-factory");
			// 获取session-factory元素下面的所有property元素
			List<Element> pro_eles = sf.elements("property");
			if (pro_eles != null && pro_eles.size() > 0) {
				for (Element each : pro_eles) {
					// 过滤掉show_sql format_sql之类的，只留connection相关
					if (each.attributeValue("name").contains("connection")) {
						String key = each.attributeValue("name").substring("connection".length() + 1);
						String value = each.getText().trim();
						hibernateCfgProperties.put(key, value);
					}

				}
			}
			// 同时记得把hbm映射文件也读取出来 <mapping resource="com/jiang/vo/Member.hbm.xml"
			// />
			List<Element> hbm_eles = sf.elements("mapping");
			if (hbm_eles != null && hbm_eles.size() > 0) {
				for (int i = 0; i < hbm_eles.size(); i++) {
					this.hbmList.add(hbm_eles.get(i).attributeValue("resource"));
				}
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @获取hbm文件
	 * @param vo
	 * @return
	 */
	private File getHbmFile(Object vo) {

		// 获取所有hbm，然后匹配vo
		for (int i = 0; i < this.hbmList.size(); i++) {
			String[] hbmdir = this.hbmList.get(i).split("\\/");
			String hbmName = hbmdir[hbmdir.length - 1];
			String voName = vo.getClass().getSimpleName();
			if (hbmName != null && hbmName.contains(voName)) {
				File hbmFile = new File(this.pro_classPath + File.separator + hbmList.get(i));
				return hbmFile;
			}
		}
		return null;
	}

	/**
	 * 解析hbm文件
	 * 
	 * @param vo
	 */
	private void parseHbmFile(File hbmFile) {

		// 解析文件
		SAXReader sax = new SAXReader();
		try {
			Document read = sax.read(hbmFile);
			Element root = read.getRootElement();
			// 获取class节点 与其对应的表与模式
			Element classEle = root.element("class");
			this.table = classEle.attributeValue("table");
			this.catalog = classEle.attributeValue("catalog");
			{
				// 这部分代码块 主要是来处理主键的
				Element idEle = classEle.element("id");
				if (idEle != null) {
					this.columAndPro_id.setIsId(true);
					this.columAndPro_id.setPropertyName(idEle.attributeValue("name"));
					this.columAndPro_id.setType(idEle.attributeValue("type"));
					this.columAndPro_id.setColumnName(idEle.element("column").attributeValue("name"));
					this.columAndPro_id.setGenerator(idEle.element("generator").attributeValue("class"));
				}
			}
			// 下面的代码块用来处理映射属性
			{
				Iterator<Element> elementIterator = classEle.elementIterator("property");
				while (elementIterator.hasNext()) {
					Element temp = elementIterator.next();
					ColumnAndProperty colAndPro = new ColumnAndProperty();
					colAndPro.setIsId(false);
					colAndPro.setPropertyName(temp.attributeValue("name"));
					colAndPro.setType(temp.attributeValue("type"));
					colAndPro.setColumnName(temp.element("column").attributeValue("name"));
					columnAndPros.put(colAndPro.getPropertyName(), colAndPro);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}
